import sys
import wx

from time import time

from LMG.Utility.constants import *

"""
Handles the torrent connection to the engine 
Features:
 - upload/download rates
 - Local settings
 - Super-seeding
 - Reannounce to tracker 
"""

################################################################

class TorrentConnections(object):
    """
    Keep track of seeding options, upload/download
    Options
    """
    def __init__(self, torrent):
        self.torrent = torrent
        
        self.engine = None
        
        self.maxupload = None

        self.maxrate = {}
        
        # upload rate bandwidth reserved for this torrent
        self.maxrate['up'] = 0.0
        self.maxrate['down'] = 0.0

        self.maxlocalrate = {}

        # Maximum upload rate not to be exceeded, defined in local settings
        self.maxlocalrate['up'] = 0
        self.maxlocalrate['down'] = 0
        
        # Snapshot of current rate
        # (taken during each pass through CyclicalTasks)
        self.rate = {}
        self.rate['up'] = 0.0
        self.rate['down'] = 0.0

        self.seedoptions = { 'uploadtime': None, 
                             'uploadratio': None }
        
        self.seedingtime = 0
        self.seedingtimeleft = self.getTargetSeedingTime()
                
        self.timeout = True

    def applyLabel(self, label):
        template = utility.labelmanager.get(label)
        if not template:
            return

        info = {}
        if template.uploadrate is not None:
            info['uploadrate'] = template.uploadrate
        if template.downloadrate is not None:
            info['downloadrate'] = template.downloadrate
        if template.maxuploads is not None:
            info['maxupload'] = template.maxuploads
            
        if template.uploadratio is not None:
            info['uploadratio'] = template.uploadratio
        if template.uploadtime is not None:
            info['uploadtime'] = template.uploadtime
        info['timeout'] = not template.distimeouts

        if template.activelabel is not None:
            info['activelabel'] = template.label
        
        self.changeLocalInfo(info)

    def clearLabel(self):
        self.changeLocalInfo({'activelabel': ""})

    def getLocalInfo(self, keys = None):
        info = {}
        if keys is None:
            keys = ['activelabel', 'uploadrate', 'downloadrate', 'maxupload', 'uploadratio', 'uploadtime', 'timeout', 'announces']
        
        if 'activelabel' in keys:
            info['activelabel'] = self.torrent.files.activelabel
        if 'uploadrate' in keys:
            info['uploadrate'] = self.getLocalRate('up')
        if 'downloadrate' in keys:
            info['downloadrate'] = self.getLocalRate('down')
        if 'maxupload' in keys:
            info['maxupload'] = self.getMaxUpload()
        if 'uploadratio' in keys:
            info['uploadratio'] = self.__trans(self.getSeedOption('uploadratio'), int)
        if 'uploadtime' in keys:
            info['uploadtime'] = self.__trans(self.getSeedOption('uploadtime'), int)
        if 'timeout' in keys:
            info['timeout'] = self.timeout
        if 'announces' in keys:
            info['announces'] = self.torrent.trackerlist                    
        return info
    
    def __trans(self, obj, buildin_type):
        v = None
        try:
            v = buildin_type(obj)
        except:
            pass
        return v
    
    def changeLocalInfo(self, info):
        """
        Change local settings
        """
        if 'activelabel' in info:
            self.torrent.files.activelabel = info['activelabel']
        if 'uploadrate' in info:
            uploadrate = self.__trans(info['uploadrate'], float)
            if uploadrate is not None:
                if uploadrate >= 0:
                    self.maxlocalrate['up'] = int(uploadrate)
                if uploadrate > 0:
                    self.maxrate['up'] = float(uploadrate)
        if 'downloadrate' in info:
            downloadrate = self.__trans(info['downloadrate'], float)
            if downloadrate is not None:
                if downloadrate >= 0:
                    self.maxlocalrate['down'] = int(downloadrate)
                if downloadrate > 0:
                    self.maxrate['down'] = float(downloadrate)
        if 'maxupload' in info:
            self.setMaxUpload(info['maxupload'])

        #active process
        self.setMaxInitiate()
        if 'uploadratio' in info:
            uploadratio = self.__trans(info['uploadratio'], int)
            if uploadratio is not None and uploadratio >= 0:
                self.setSeedOption('uploadratio', info['uploadratio'])
        if 'uploadtime' in info:
            uploadtime = self.__trans(info['uploadtime'], int)
            if uploadtime is not None and uploadtime >= 0:
                self.setSeedOption('uploadtime', info['uploadtime'])
        if 'timeout' in info:
            timeout = self.__trans(info['timeout'], bool)
            if timeout is not None:
                self.timeout = timeout
        if 'announces' in info:
            if self.torrent.trackerlist != info['announces']:
                self.torrent.trackerlist = info['announces']
                self.torrent.updatetrackerlist()
        
        self.torrent.torrentconfig.writeUploadParams()
        
        # Double-check to see if we're still done uploading
        self.torrent.status.isDoneUploading()
                          
    def superSeed(self, caller=""):
        """
        Enable superseed mode
        """
        if self.torrent.status.value == STATUS_SUPERSEED:
            # Don't do anything if superseed is already enabled
            return

        if not self.torrent.status.completed:
            # Don't do anything if the file isn't complete
            return
        
        if not self.torrent.status.isActive():
            if caller not in ["web", "command"]:
                #dialogbox running torrent before using super-seed mode
                dlg = wx.MessageDialog(None, _('Torrent must be active'), _('Error'), wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
            return
            
        if (self.engine.dow is not None):
            #Enter super-seed mode
            self.torrent.status.updateStatus(STATUS_SUPERSEED)
            # one way change, don't go back
            self.engine.dow.set_super_seed()
            # change BTstatus to super-seeding
            self.torrent.updateColumns([COL_BTSTATUS])
            
    def startEngine(self, status = STATUS_ACTIVE):
        """
        Start torrent download engine
        """
        self.torrent.status.updateStatus(status)

        self.torrent.updateSingleItemStatus() # BT Status

        utility.controller.add(self.torrent)

    def stopEngine(self, update = True):
        """
        Stop torrent download engine
        """
        if self.engine is not None:
            self.engine.shutdown()

    def resetUploadParams(self):
        """
        Reset the torrent's upload parameters
        """
        self.setMaxInitiate()
        
        self.torrent.torrentconfig.writeUploadParams()

        # Double-check to see if we're still done uploading
        self.torrent.status.isDoneUploading()

    def setRate(self, speed = None, dir = "up"):       
        """
        Set the torrent's upload or download rate limit
        """
        if speed is None:
            speed = self.getLocalRate(dir)
            if speed == 0:
                speed = self.maxrate[dir]
        speed = float(speed)
                               
        try:
            if self.engine is not None and self.engine.working:
                # Speed is unchanged, shouldn't need to do anything
                if self.engine.ratesetting[dir] == speed:
                    return
                
                if dir == "up":
                    # Set upload rate
                    self.engine.dow.setUploadRate(speed)
                elif not self.torrent.status.completed:
                    # Set download rate
                    # (only makes sense if not complete)
                    self.engine.dow.setDownloadRate(speed)
                
                self.engine.ratesetting[dir] = speed
        except:
            pass
        
    def getLocalRate(self, dir, boolean = False):
        """
        Get the local upload or download rate limit for this torrent
        """
        try:
            value = int(self.maxlocalrate[dir])
        except:
            value = 0
        
        if boolean:
            return value > 0
        else:
            return value
        
    def getMaxUpload(self, localonly = False):
        """
        Return max upload slots
        """
        value = self.maxupload
        if value is None and not localonly:
            value = utility.config.Read('maxupload', "int")
        
        return value
        
    def getMaxInitiate(self):
        maxupload = self.getMaxUpload()
        max_initiate = utility.config.Read('max_initiate', "int")
        
        if max_initiate == 0:
            return None
        if maxupload < 4:
            max_initiate = min(12, max_initiate)
        elif maxupload < 30:
            max_initiate = min(40, max_initiate)
        else:
            max_initiate = min(maxupload + 10, max_initiate)
            
        return max_initiate

    def setMaxInitiate(self):
        if self.engine is not None:
            self.engine.dow.setConns(self.getMaxUpload())

            max_initiate = self.getMaxInitiate()
            if max_initiate is not None:
                self.engine.dow.setInitiate(max_initiate)
        
    def setMaxUpload(self, value = None):
        """
        Set max upload slots
        """
        if value is None:
            value = self.maxupload
        
        try:
            value = int(value)
        except:
            value = 0
        
        default = utility.config.Read('maxupload', "int")
        if value == default or value <= 0:
            self.maxupload = None
        else:
            self.maxupload = value
        
    def getSeedOption(self, param, localonly = False):
        """
        Return a seed option
        """
        value = self.seedoptions[param]
        if value is None and not localonly:
            value = utility.config.Read(param)

        return value
        
    def setSeedOption(self, param, value):
        """
        Set a seed option
        """
        default = utility.config.Read(param)
        if str(value) == str(default):
            self.seedoptions[param] = None
        else:
            self.seedoptions[param] = value
    
    def getTargetSeedingTime(self):
        """
        Return the total seeding time option
        """
        uploadtime = self.getSeedOption('uploadtime')         
        return int(uploadtime)
        
        
    def reannounce(self, arg = ""):
        """
        Reannounce to tracker/s
        """
        # if torrent is not running.. can't reannounce
        if not self.torrent.status.isActive(checking = False):
            return

        if (time() - self.engine.reannouncelast > 60):
            self.engine.reannouncelast = time()
            
            try:
                if (self.engine.dow is not None):
                    if not arg:
                        self.engine.dow.reannounce()
                    else:
                        self.engine.dow.reannounce(arg)
            except:
                pass
